package za.um.td.entity.level;

import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.ImageIcon;

import jmetest.terrain.TestProceduralSplatTexture;

import org.w3c.dom.Node;

import za.um.td.state.BasicGameStateSingleton;
import za.um.td.util.XMLUtil;
import za.um.td.util.texture.TextureSet;
import za.um.td.util.texture.TextureSetManager;
import za.um.td.util.texture.Texturete;

import com.jme.entity.Entity;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.scene.PassNode;
import com.jme.scene.PassNodeState;
import com.jme.scene.Spatial;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.AbstractHeightMap;
import com.jmex.terrain.util.ImageBasedHeightMap;
import com.jmex.terrain.util.RawHeightMap;

public class Tile extends Entity {
	
	private static final Logger LOG = Logger.getLogger(Entity.class.getName());
	
	public final static String NODE_TILE = "tile";
	public final static String NODE_TERRAIN = "terrain";
	public final static String ATRI_TEXTURESET = "textureset";
	public final static String ATRI_USEDMAPS = "usedmaps";
	public final static String ATRI_SIZE = "size";
	
	private String textureset = null;
	private String usedmaps = null;
	private int size = -1;
	//private float heightMap[] = null;
	
	private Node terrain = null;
	private Spatial splatTerrain;
    private Spatial reflectionTerrain;
    
    private TerrainPage page;
    
    private AbstractHeightMap heightMap = null;
	
	private DisplaySystem display = null;
	
	
	private float textureScale = 0.07f;
    private float globalSplatScale = 90.0f;
    
    private String tile = null;
    
    private Vector3f centreLocation = null;
    private Vector3f terrainScale = null;
    private float heightScale = 0;
    
	public Tile(String id) {
		super(id);
		tile = id.substring(5, id.length());
		display = BasicGameStateSingleton.game.getDisplay();
	}
	
	public void init(URL xmlUrl, Vector3f centreLocation, Vector3f terrainScale, float heightScale)
	{
		this.centreLocation = centreLocation;
		this.terrainScale = terrainScale;
		this.heightScale = heightScale;
		
		load(xmlUrl);
		
		createTile(centreLocation, terrainScale, heightScale);
	}
	
	private void load(URL xmlUrl)
	{
		LOG.log(Level.INFO,"xmlUrl="+xmlUrl);
		try	{
			Node tileNode = XMLUtil.loadDocument(xmlUrl.openStream(), Tile.NODE_TILE);
			
			Node terrainNode = XMLUtil.getChildNode(tileNode, Tile.NODE_TERRAIN);
			
			textureset = XMLUtil.getAttribute(terrainNode, Tile.ATRI_TEXTURESET);
			size = XMLUtil.getIntAttribute(terrainNode, Tile.ATRI_SIZE);
			usedmaps = XMLUtil.getAttribute(terrainNode, Tile.ATRI_USEDMAPS);
			
			String strHeightMap = terrainNode.getTextContent();
			
			String vStrHeightMap[] = strHeightMap.split(" ");
			
			float heightMap[] = new float[this.size*this.size+1];
			
			LOG.log(Level.INFO,"heightMap.length="+heightMap.length);
			for(int i=0; i < this.size*this.size; i++)
			{
				//LOG.log(Level.INFO,"vStrHeightMap["+i+"]="+vStrHeightMap[i]);
				heightMap[i] = XMLUtil.str2float(vStrHeightMap[i]);
			}
			
			this.heightMap = new RawHeightMap(heightMap);
		} 
		catch (IOException e) {
			LOG.log(Level.SEVERE, e.getMessage() + "\t" + e.getStackTrace());			
		}
		catch(NullPointerException e) {
			LOG.log(Level.SEVERE, e.getMessage() + "\t" + e.getStackTrace());
		}
	}
	
	
	private void createTile(Vector3f centreLocation, Vector3f terrainScale, float heightScale) {
		
		LOG.log(Level.INFO,"heightMap.getSize()="+heightMap.getSize());


        heightMap.setHeightScale(heightScale);
        page = new TerrainPage("Terrain", 64, heightMap.getSize()+1,
                terrainScale, heightMap.getHeightMap());

        page.setLocalTranslation(centreLocation);
        page.setDetailTexture(1, 1);

        // alpha used for blending the passnodestates together
        BlendState as = display.getRenderer().createBlendState();
        as.setBlendEnabled(true);
        as.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
        as.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha);
        as.setTestEnabled(true);
        as.setTestFunction(BlendState.TestFunction.GreaterThan);
        as.setEnabled(true);

        // alpha used for blending the lightmap
        BlendState as2 = display.getRenderer().createBlendState();
        as2.setBlendEnabled(true);
        as2.setSourceFunction(BlendState.SourceFunction.DestinationColor);
        as2.setDestinationFunction(BlendState.DestinationFunction.SourceColor);
        as2.setTestEnabled(true);
        as2.setTestFunction(BlendState.TestFunction.GreaterThan);
        as2.setEnabled(true);

        TextureSetManager tm = TextureSetManager.getInstance();
        
        TextureSet texSet = tm.get(this.textureset);

        PassNode splattingPassNode = new PassNode("SplatPassNode");
        splattingPassNode.attachChild(page);
        
        List<Texturete> texList = texSet.getTextureteList();
        
        TextureState ts1 = texList.get(0).createSplatTextureState(
        		new Vector3f(globalSplatScale, globalSplatScale, 1.0f), 
        		null);
        
        PassNodeState passNodeState = new PassNodeState();
        passNodeState.setPassState(ts1);
        splattingPassNode.addPass(passNodeState);
        
        if(texList.size()>1)
        {
	        for(int i=1; i < texList.size(); i++)
	        {
	        	Texturete t = texList.get(i);
	        	TextureState ts = 	t.createSplatTextureState(
	        			new Vector3f(globalSplatScale, globalSplatScale, 1.0f),
	                    "za/um/td/data/level/tile/maps/alpha_"+tile+"_"+(i-1)+".png");
	        	
	        	passNodeState = new PassNodeState();
	            passNodeState.setPassState(ts);
	            passNodeState.setPassState(as);
	            splattingPassNode.addPass(passNodeState);
	        }
        }
        
   
         
        TextureState ts6 = Texturete.createLightmapTextureState(
        		"za/um/td/data/level/tile/maps/light_"+tile+".png");
        

        passNodeState = new PassNodeState();
        passNodeState.setPassState(ts6);
        passNodeState.setPassState(as2);
        splattingPassNode.addPass(passNodeState);
        // //////////////////// PASS STUFF END

        
        // lock some things to increase the performance
        splattingPassNode.lockBounds();
        splattingPassNode.lockTransforms();
        splattingPassNode.lockShadows();

        splatTerrain = splattingPassNode;
        splatTerrain.setCullHint(Spatial.CullHint.Dynamic);
        
        this.setSpatial(splatTerrain);

    }
	
	
	
	public String getTextureset() {
		return textureset;
	}

	public void setTextureset(String textureset) {
		this.textureset = textureset;
	}

	public String getUsedmaps() {
		return usedmaps;
	}

	public void setUsedmaps(String usedmaps) {
		this.usedmaps = usedmaps;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	public AbstractHeightMap getHeightMap() {
		return heightMap;
	}

	public void setHeightMap(AbstractHeightMap heightMap) {
		this.heightMap = heightMap;
	}
	
	public float getHeight(Vector3f location) {
		return page.getHeight(location);
	}
	
	public TerrainPage getTerrainPage()
	{
		return page;
	}

	public float getTextureScale() {
		return textureScale;
	}

	public void setTextureScale(float textureScale) {
		this.textureScale = textureScale;
	}

	public float getGlobalSplatScale() {
		return globalSplatScale;
	}

	public void setGlobalSplatScale(float globalSplatScale) {
		this.globalSplatScale = globalSplatScale;
	}

	public Vector3f getCentreLocation() {
		return centreLocation;
	}

	public void setCentreLocation(Vector3f centreLocation) {
		this.centreLocation = centreLocation;
	}

	public Vector3f getTerrainScale() {
		return terrainScale;
	}

	public void setTerrainScale(Vector3f terrainScale) {
		this.terrainScale = terrainScale;
	}

	public float getHeightScale() {
		return heightScale;
	}

	public void setHeightScale(float heightScale) {
		this.heightScale = heightScale;
	}
	
	
}
